﻿using System;
using System.Collections.Generic;
using System.Text;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.Core.Modelling.Primitives;

namespace ITAP.ShprossesBuilder.Modelling.Components.Constraints
{
    [Serializable]
    /// <summary>
    /// Совмещение начального конца компонента (IEndedComponent.StartPoint) с ближайшим концом компонента определённого типа, 
    /// который к нему приконнекчен. Оба компонента должны реализовывать IEndedComponent.
    /// </summary>
    public class ConnectedComponentsEndsJoinConstraint : IConstraint
    {
        private EndType endToJoin;
        private ConstraintAction action;

        public ConstraintAction Action
        {
            get { return action; }
        }
        private bool enabled = true;
        private Type typeOfConnectedComponent;

        public ConnectedComponentsEndsJoinConstraint(Type typeOfConnectedComponent, EndType endToJoin, ConstraintAction action)
        {
            this.endToJoin = endToJoin;
            this.action = action;
            this.typeOfConnectedComponent = typeOfConnectedComponent;
        }
        #region IConstraint Members

        public bool Check(IComponent component)
        {
            int connectedComponentsCount = 0;
            int correctEnds = 0;

            if(!(component is IEndedComponent)) return true;

            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if(!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponent) continue;

                connectedComponentsCount++;
                IEndedComponent endedComponent = (IEndedComponent)component;
                IEndedComponent endedConnectedComponent = (IEndedComponent)connectedComponent;

                if (endedConnectedComponent == endedComponent.StartConnectedComponent)
                {
                    if (endToJoin == EndType.Start)
                    {
                        if (endedComponent.StartPoint.Equals(endedConnectedComponent.StartPoint))
                            correctEnds++;
                    }
                    else if (endToJoin == EndType.End)
                    {
                        if (endedComponent.StartPoint.Equals(endedConnectedComponent.EndPoint))
                            correctEnds++;
                    }
                    else
                        throw new Exception();
                }
                else if (endedConnectedComponent == endedComponent.EndConnectedComponent)
                {
                    if (endToJoin == EndType.Start)
                    {
                        if (endedComponent.EndPoint.Equals(endedConnectedComponent.StartPoint))
                            correctEnds++;
                    }
                    else if (endToJoin == EndType.End)
                    {
                        if (endedComponent.EndPoint.Equals(endedConnectedComponent.EndPoint))
                            correctEnds++;
                    }

                }
                else
                    // Если тип приконнекченного компонента совпадает с тем, который должен быть приконнекчен 
                    // к началу или концу компонента, но его там нет, то значит, что он приконнекчен "сбоку" 
                    // (например, StickConnector для соединения "профиль в профиль").
                    continue;
                    //throw new Exception();
            }
            if (correctEnds < connectedComponentsCount)
                return false;

            return true;
        }

        public void Fix(IComponent component)
        {
            if (!enabled) return;
            if (!(component is IEndedComponent)) return;

            foreach (IComponent connectedComponent in component.ConnectedComponents)
            {
                if (!(connectedComponent is IEndedComponent)) continue;
                if (connectedComponent.GetType() != typeOfConnectedComponent) continue;


                IEndedComponent endedComponent = (IEndedComponent)component;
                IEndedComponent endedConnectedComponent = (IEndedComponent)connectedComponent;

                if (endedConnectedComponent == endedComponent.StartConnectedComponent)
                {
                    if (this.endToJoin == EndType.Start)
                    {
                        ((IEndedPrimitive)endedComponent.Primitives[0]).MoveStartPoint(endedConnectedComponent.StartPoint);
                 //       endedComponent.MoveStartPointTo(endedConnectedComponent.StartPoint);
                        endedComponent.Primitives[0].Update();
                    }
                    else if (this.endToJoin == EndType.End)
                    {
                        ((IEndedPrimitive)endedComponent.Primitives[0]).MoveStartPoint(endedConnectedComponent.EndPoint);
                     //   endedComponent.MoveStartPointTo(endedConnectedComponent.EndPoint);
                        endedComponent.Primitives[0].Update();
                    }
                    else
                        throw new Exception();
                }
                else if (endedConnectedComponent == endedComponent.EndConnectedComponent)
                {
                    if (this.endToJoin == EndType.Start)
                    {
                        ((IEndedPrimitive)endedComponent.Primitives[0]).MoveEndPoint(endedConnectedComponent.StartPoint);
                      //  endedComponent.MoveEndPointTo(endedConnectedComponent.StartPoint);

                        endedComponent.Primitives[0].Update();
                    }
                    else if (this.endToJoin == EndType.End)
                    {
                        ((IEndedPrimitive)endedComponent.Primitives[0]).MoveEndPoint(endedConnectedComponent.EndPoint);
                        //endedComponent.MoveEndPointTo(endedConnectedComponent.EndPoint);

                        endedComponent.Primitives[0].Update();
                    }
                    else throw new Exception();
                }
                else
                    // Если тип приконнекченного компонента - тот, который зарегистрирован в ограничении, но 
                    // на концы компонента не приконнекчены к компоненту, который приконнекчен, тогда это означает, 
                    // что приконнекченный компонент приконнекчен "сбоку" (например, это StickConnector при соединении
                    // "профиль в профиль").
                    continue;
                    //throw new Exception();


            }
        }

        public bool Enabled
        {
            get
            {
                return enabled;
            }
            set
            {
                enabled = value;
            }
        }

        #endregion



        #region ICloneable Members

        public object Clone()
        {
            ConnectedComponentsEndsJoinConstraint clone = new ConnectedComponentsEndsJoinConstraint(typeOfConnectedComponent, endToJoin, action);
            return clone;
        }

        #endregion
    }
}
